home *** CD-ROM | disk | FTP | other *** search
/ Young Minds / Young Minds Interactive CD-ROM.ISO / rogue / patch1 / machdep.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-05-30  |  16.1 KB  |  588 lines

  1. /*
  2.  * machdep.c
  3.  *
  4.  * This source herein may be modified and/or distributed by anybody who
  5.  * so desires, with the following restrictions:
  6.  *    1.)  No portion of this notice shall be removed.
  7.  *    2.)  Credit shall not be taken for the creation of this source.
  8.  *    3.)  This code is not to be traded, sold, or used for personal
  9.  *         gain or profit.
  10.  *
  11.  */
  12.  
  13. /* Included in this file are all system dependent routines.  Extensive use
  14.  * of #ifdef's will be used to compile the appropriate code on each system:
  15.  *
  16.  *    UNIX:        all UNIX systems.
  17.  *    UNIX_BSD4_2: UNIX BSD 4.2 and later, UTEK, (4.1 BSD too?)
  18.  *    UNIX_SYSV:   UNIX system V
  19.  *    UNIX_V7:     UNIX version 7
  20.  *
  21.  * All UNIX code should be included between the single "#ifdef UNIX" at the
  22.  * top of this file, and the "#endif UNIX" at the bottom.
  23.  * 
  24.  * To change a routine to include a new UNIX system, simply #ifdef the
  25.  * existing routine, as in the following example:
  26.  *
  27.  *   To make a routine compatible with UNIX system V, change the first
  28.  *   function to the second:
  29.  *
  30.  *      md_function()
  31.  *      {
  32.  *         code;
  33.  *      }
  34.  *
  35.  *      md_function()
  36.  *      {
  37.  *      #ifdef UNIX_SYSV
  38.  *         sysVcode;
  39.  *      #else
  40.  *         code;
  41.  *      #endif /* UNIX_SYSV */
  42.  *      }
  43.  *
  44.  * Appropriate variations of this are of course acceptible.
  45.  * The use of "#elseif" is discouraged because of non-portability.
  46.  * If the correct #define doesn't exist, "UNIX_SYSV" in this case, make it up
  47.  * and insert it in the list at the top of the file.  Alter the CFLAGS
  48.  * in you Makefile appropriately.
  49.  *
  50.  */
  51.  
  52. #ifdef UNIX
  53.  
  54. #include <stdio.h>
  55. #include <sys/types.h>
  56. #include <sys/file.h>
  57. #include <sys/stat.h>
  58.  
  59. #ifdef UNIX_SYSV
  60. #include <time.h>
  61. #include <termio.h>
  62. #endif /* UNIX_SYSV */
  63. #ifdef UNIX_BSD4_2
  64. #include <sys/time.h>
  65. #include <sgtty.h>
  66. #endif /* UNIX_BSD4_2 */
  67.  
  68. #include <signal.h>
  69. #include "rogue.h"
  70.  
  71. /* md_slurp:
  72.  *
  73.  * This routine throws away all keyboard input that has not
  74.  * yet been read.  It is used to get rid of input that the user may have
  75.  * typed-ahead.
  76.  *
  77.  * This function is not necessary, so it may be stubbed.  The might cause
  78.  * message-line output to flash by because the game has continued to read
  79.  * input without waiting for the user to read the message.  Not such a
  80.  * big deal.
  81.  */
  82.  
  83. md_slurp()
  84. {
  85.     long ln;
  86.  
  87. #ifdef UNIX_BSD4_2
  88.     ioctl(0, TIOCFLUSH, &ln);
  89. #endif /* UNIX_BSD4_2 */
  90. #ifdef UNIX_SYSV
  91.     ioctl(0, TCFLSH, &ln);
  92. #endif /* UNIX_SYSV */
  93.  
  94.     for (ln = stdin->_cnt; ln > 0; ln--) {
  95.         (void) getchar();
  96.     }
  97. }
  98.  
  99. /* md_control_keyboard():
  100.  *
  101.  * This routine is much like md_cbreak_no_echo_nonl() below.  It sets up the
  102.  * keyboard for appropriate input.  Specifically, it prevents the tty driver
  103.  * from stealing characters.  For example, ^Y is needed as a command
  104.  * character, but the tty driver intercepts it for another purpose.  Any
  105.  * such behavior should be stopped.  This routine could be avoided if
  106.  * we used RAW mode instead of CBREAK.  But RAW mode does not allow the
  107.  * generation of keyboard signals, which the program uses.
  108.  *
  109.  * The parameter 'mode' when true, indicates that the keyboard should
  110.  * be set up to play rogue.  When false, it should be restored if
  111.  * necessary.
  112.  *
  113.  * This routine is not strictly necessary and may be stubbed.  This may
  114.  * cause certain command characters to be unavailable.
  115.  */
  116.  
  117. md_control_keybord(mode)
  118. boolean mode;
  119. {
  120.     static boolean called_before = 0;
  121. #ifdef UNIX_BSD4_2
  122.     static struct ltchars ltc_orig;
  123.     static struct tchars tc_orig;
  124.     struct ltchars ltc_temp;
  125.     struct tchars tc_temp;
  126. #endif /* UNIX_BSD4_2 */
  127. #ifdef UNIX_SYSV
  128.     static struct termio _oldtty;
  129.     struct termio _tty;
  130. #endif /* UNIX_SYSV */
  131.  
  132.     if (!called_before) {
  133.         called_before = 1;
  134. #ifdef UNIX_BSD4_2
  135.         ioctl(0, TIOCGETC, &tc_orig);
  136.         ioctl(0, TIOCGLTC, <c_orig);
  137. #endif /* UNIX_BSD4_2 */
  138. #ifdef UNIX_SYSV
  139.         ioctl(0, TCGETA, &_oldtty);
  140. #endif /* UNIX_SYSV */
  141.     }
  142. #ifdef UNIX_BSD4_2
  143.     ltc_temp = ltc_orig;
  144.     tc_temp = tc_orig;
  145. #endif /* UNIX_BSD4_2 */
  146. #ifdef UNIX_SYSV
  147.     _tty = _oldtty;
  148. #endif /* UNIX_SYSV */
  149.  
  150.     if (!mode) {
  151. #ifdef UNIX_BSD4_2
  152.         ltc_temp.t_suspc = ltc_temp.t_dsuspc = -1;
  153.         ltc_temp.t_rprntc = ltc_temp.t_flushc = -1;
  154.         ltc_temp.t_werasc = ltc_temp.t_lnextc = -1;
  155.         tc_temp.t_startc = tc_temp.t_stopc = -1;
  156. #endif /* UNIX_BSD4_2 */
  157. #ifdef UNIX_SYSV
  158.         _tty.c_cc[VSWTCH] = CNSWTCH;
  159. #endif /* UNIX_SYSV */
  160.     }
  161. #ifdef UNIX_BSD4_2
  162.     ioctl(0, TIOCSETC, &tc_temp);
  163.     ioctl(0, TIOCSLTC, <c_temp);
  164. #endif /* UNIX_BSD4_2 */
  165. #ifdef UNIX_SYSV
  166.     ioctl(0, TCSETA, &_tty);
  167. #endif /* UNIX_SYSV */
  168. }
  169.  
  170. /* md_heed_signals():
  171.  *
  172.  * This routine tells the program to call particular routines when
  173.  * certain interrupts/events occur:
  174.  *
  175.  *      SIGINT: call onintr() to interrupt fight with monster or long rest.
  176.  *      SIGQUIT: call byebye() to check for game termination.
  177.  *      SIGHUP: call error_save() to save game when terminal hangs up.
  178.  *
  179.  *        On VMS, SIGINT and SIGQUIT correspond to ^C and ^Y.
  180.  *
  181.  * This routine is not strictly necessary and can be stubbed.  This will
  182.  * mean that the game cannot be interrupted properly with keyboard
  183.  * input, this is not usually critical.
  184.  */
  185.  
  186. md_heed_signals()
  187. {
  188.     signal(SIGINT, onintr);
  189.     signal(SIGQUIT, byebye);
  190.     signal(SIGHUP, error_save);
  191. }
  192.  
  193. /* md_ignore_signals():
  194.  *
  195.  * This routine tells the program to completely ignore the events mentioned
  196.  * in md_heed_signals() above.  The event handlers will later be turned on
  197.  * by a future call to md_heed_signals(), so md_heed_signals() and
  198.  * md_ignore_signals() need to work together.
  199.  *
  200.  * This function should be implemented or the user risks interrupting
  201.  * critical sections of code, which could cause score file, or saved-game
  202.  * file, corruption.
  203.  */
  204.  
  205. md_ignore_signals()
  206. {
  207.     signal(SIGQUIT, SIG_IGN);
  208.     signal(SIGINT, SIG_IGN);
  209.     signal(SIGHUP, SIG_IGN);
  210. }
  211.  
  212. /* md_get_file_id():
  213.  *
  214.  * This function returns an integer that uniquely identifies the specified
  215.  * file.  It need not check for the file's existence.  In UNIX, the inode
  216.  * number is used.
  217.  *
  218.  * This function need not be implemented.  To stub the routine, just make
  219.  * it return 0.  This will make the game less able to prevent users from
  220.  * modifying saved-game files.  This is probably no big deal.
  221.  */
  222.  
  223. int
  224. md_get_file_id(fname)
  225. char *fname;
  226. {
  227.     struct stat sbuf;
  228.  
  229.     if (stat(fname, &sbuf)) {
  230.         return(-1);
  231.     }
  232.     return((int) sbuf.st_ino);
  233. }
  234.  
  235. /* md_link_count():
  236.  *
  237.  * This routine returns the number of hard links to the specified file.
  238.  *
  239.  * This function is not strictly necessary.  On systems without hard links
  240.  * this routine can be stubbed by just returning 1.
  241.  */
  242.  
  243. int
  244. md_link_count(fname)
  245. char *fname;
  246. {
  247.     struct stat sbuf;
  248.  
  249.     stat(fname, &sbuf);
  250.     return((int) sbuf.st_nlink);
  251. }
  252.  
  253. /* md_gct(): (Get Current Time)
  254.  *
  255.  * This function returns the current year, month(1-12), day(1-31), hour(0-23),
  256.  * minute(0-59), and second(0-59).  This is used for identifying the time
  257.  * at which a game is saved.
  258.  *
  259.  * This function is not strictly necessary.  It can be stubbed by returing
  260.  * zeros instead of the correct year, month, etc.  If your operating
  261.  * system doesn't provide all of the time units requested here, then you
  262.  * can provide only those that it does, and return zeros for the others.
  263.  * If you cannot provide good time values, then users may be able to copy
  264.  * saved-game files and play them.  
  265.  */
  266.  
  267. md_gct(rt_buf)
  268. struct rogue_time *rt_buf;
  269. {
  270.     struct tm *t, *localtime();
  271.     long seconds;
  272.  
  273.     time(&seconds);
  274.     t = localtime(&seconds);
  275.  
  276.     rt_buf->year = t->tm_year;
  277.     rt_buf->month = t->tm_mon + 1;
  278.     rt_buf->day = t->tm_mday;
  279.     rt_buf->hour = t->tm_hour;
  280.     rt_buf->minute = t->tm_min;
  281.     rt_buf->second = t->tm_sec;
  282. }
  283.  
  284. /* md_gfmt: (Get File Modification Time)
  285.  *
  286.  * This routine returns a file's date of last modification in the same format
  287.  * as md_gct() above.
  288.  *
  289.  * This function is not strictly necessary.  It is used to see if saved-game
  290.  * files have been modified since they were saved.  If you have stubbed the
  291.  * routine md_gct() above by returning constant values, then you may do
  292.  * exactly the same here.
  293.  * Or if md_gct() is implemented correctly, but your system does not provide
  294.  * file modification dates, you may return some date far in the past so
  295.  * that the program will never know that a saved-game file being modified.  
  296.  * You may also do this if you wish to be able to restore games from
  297.  * saved-games that have been modified.
  298.  */
  299.  
  300. md_gfmt(fname, rt_buf)
  301. char *fname;
  302. struct rogue_time *rt_buf;
  303. {
  304.     struct stat sbuf;
  305.     long seconds;
  306.     struct tm *t;
  307.  
  308.     stat(fname, &sbuf);
  309.     seconds = (long) sbuf.st_mtime;
  310.     t = localtime(&seconds);
  311.  
  312.     rt_buf->year = t->tm_year;
  313.     rt_buf->month = t->tm_mon + 1;
  314.     rt_buf->day = t->tm_mday;
  315.     rt_buf->hour = t->tm_hour;
  316.     rt_buf->minute = t->tm_min;
  317.     rt_buf->second = t->tm_sec;
  318. }
  319.  
  320. /* md_df: (Delete File)
  321.  *
  322.  * This function deletes the specified file, and returns true (1) if the
  323.  * operation was successful.  This is used to delete saved-game files
  324.  * after restoring games from them.
  325.  *
  326.  * Again, this function is not strictly necessary, and can be stubbed
  327.  * by simply returning 1.  In this case, saved-game files will not be
  328.  * deleted and can be replayed.
  329.  */
  330.  
  331. boolean
  332. md_df(fname)
  333. char *fname;
  334. {
  335.     if (unlink(fname)) {
  336.         return(0);
  337.     }
  338.     return(1);
  339. }
  340.  
  341. /* md_gln: (Get login name)
  342.  *
  343.  * This routine returns the login name of the user.  This string is
  344.  * used mainly for identifying users in score files.
  345.  *
  346.  * A dummy string may be returned if you are unable to implement this
  347.  * function, but then the score file would only have one name in it.
  348.  */
  349.  
  350. char *
  351. md_gln()
  352. {
  353.     char *getlogin();
  354.     char *t;
  355.  
  356.     t = getlogin();
  357.     return(t);
  358. }
  359.  
  360. /* md_sleep:
  361.  *
  362.  * This routine causes the game to pause for the specified number of
  363.  * seconds.
  364.  *
  365.  * This routine is not necessary at all, and can be stubbed with no ill
  366.  * effects.
  367.  */
  368.  
  369. md_sleep(nsecs)
  370. int nsecs;
  371. {
  372.     (void) sleep(nsecs);
  373. }
  374.  
  375. /* md_getenv()
  376.  *
  377.  * This routine gets certain values from the user's environment.  These
  378.  * values are strings, and each string is identified by a name.  The names
  379.  * of the values needed, and their use, is as follows:
  380.  *
  381.  *   TERMCAP
  382.  *     The name of the users's termcap file, NOT the termcap entries
  383.  *     themselves.  This is used ONLY if the program is compiled with
  384.  *     CURSES defined (-DCURSES).  Even in this case, the program need
  385.  *     not find a string for TERMCAP.  If it does not, it will use the
  386.  *     default termcap file as returned by md_gdtcf();
  387.  *   TERM
  388.  *     The name of the users's terminal.  This is used ONLY if the program
  389.  *     is compiled with CURSES defined (-DCURSES).  In this case, the string
  390.  *     value for TERM must be found, or the routines in curses.c cannot
  391.  *     function, and the program will quit.
  392.  *   ROGUEOPTS
  393.  *     A string containing the various game options.  This need not be
  394.  *     defined.
  395.  *   HOME
  396.  *     The user's home directory.  This is only used when the user specifies
  397.  *     '~' as the first character of a saved-game file.  This string need
  398.  *     not be defined.
  399.  *
  400.  * If your system does not provide a means of searching for these values,
  401.  * you will have to do it yourself.  None of the values above really need
  402.  * to be defined except TERM when the program is compiled with CURSES
  403.  * defined.  In this case, as a bare minimum, you can check the 'name'
  404.  * parameter, and if it is "TERM" find the terminal name and return that,
  405.  * else return zero.  If the program is not compiled with CURSES, you can
  406.  * get by with simply always returning zero.  Returning zero indicates
  407.  * that their is no defined value for the given string.
  408.  */
  409.  
  410. char *
  411. md_getenv(name)
  412. char *name;
  413. {
  414.     char *value;
  415.     char *getenv();
  416.  
  417.     value = getenv(name);
  418.  
  419.     return(value);
  420. }
  421.  
  422. /* md_malloc()
  423.  *
  424.  * This routine allocates, and returns a pointer to, the specified number
  425.  * of bytes.  This routines absolutely MUST be implemented for your
  426.  * particular system or the program will not run at all.  Return zero
  427.  * when no more memory can be allocated.
  428.  */
  429.  
  430. char *
  431. md_malloc(n)
  432. int n;
  433. {
  434.     char *malloc();
  435.     char *t;
  436.  
  437.     t = malloc(n);
  438.     return(t);
  439. }
  440.  
  441. /* md_gseed() (Get Seed)
  442.  *
  443.  * This function returns a seed for the random number generator (RNG).  This
  444.  * seed causes the RNG to begin generating numbers at some point in it's
  445.  * sequence.  Without a random seed, the RNG will generate the same set
  446.  * of numbers, and every game will start out exactly the same way.  A good
  447.  * number to use is the process id, given by getpid() on most UNIX systems.
  448.  *
  449.  * You need to find some single random integer, such as:
  450.  *   process id.
  451.  *   current time (minutes + seconds) returned from md_gct(), if implemented.
  452.  *   
  453.  * It will not help to return "get_rand()" or "rand()" or the return value of
  454.  * any pseudo-RNG.  If you don't have a random number, you can just return 1,
  455.  * but this means your games will ALWAYS start the same way, and will play
  456.  * exactly the same way given the same input.
  457.  */
  458.  
  459. md_gseed()
  460. {
  461.     return(getpid());
  462. }
  463.  
  464. /* md_exit():
  465.  *
  466.  * This function causes the program to discontinue execution and exit.
  467.  * This function must be implemented or the program will continue to
  468.  * hang when it should quit.
  469.  */
  470.  
  471. md_exit(status)
  472. int status;
  473. {
  474.     exit(status);
  475. }
  476.  
  477. /* If you have a viable curses/termlib library, then use it and don't bother
  478.  * implementing the routines below.  And don't compile with -DCURSES.
  479.  */
  480.  
  481. #ifdef CURSES
  482.  
  483. /* md_cbreak_no_echo_nonl:
  484.  *
  485.  * This routine sets up some terminal characteristics.  The tty-driver
  486.  * must be told to:
  487.  *   1.)  Not echo input.
  488.  *   2.)  Transmit input characters immediately upon typing. (cbreak mode)
  489.  *   3.)  Move the cursor down one line, without changing column, and
  490.  *        without generating a carriage-return, when it
  491.  *        sees a line-feed.  This is only necessary if line-feed is ever
  492.  *        used in the termcap 'do' (cursor down) entry, in which case,
  493.  *        your system should must have a way of accomplishing this.
  494.  *
  495.  * When the parameter 'on' is true, the terminal is set up as specified
  496.  * above.  When this parameter is false, the terminal is restored to the
  497.  * original state.
  498.  *
  499.  * Raw mode should not to be used.  Keyboard signals/events/interrupts should
  500.  * be sent, although they are not strictly necessary.  See notes in
  501.  * md_heed_signals().
  502.  *
  503.  * This function must be implemented for rogue to run properly if the
  504.  * program is compiled with CURSES defined to use the enclosed curses
  505.  * emulation package.  If you are not using this, then this routine is
  506.  * totally unnecessary.
  507.  * 
  508.  * Notice that information is saved between calls.  This is used to
  509.  * restore the terminal to an initial saved state.
  510.  *
  511.  */
  512.  
  513. md_cbreak_no_echo_nonl(on)
  514. boolean on;
  515. {
  516. #ifdef UNIX_BSD4_2
  517.     static struct sgttyb tty_buf;
  518.     static int tsave_flags;
  519.  
  520.     if (on) {
  521.         ioctl(0, TIOCGETP, &tty_buf);
  522.         tsave_flags = tty_buf.sg_flags;
  523.         tty_buf.sg_flags |= CBREAK;
  524.         tty_buf.sg_flags &= ~(ECHO | CRMOD);    /* CRMOD: see note 3 above */
  525.         ioctl(0, TIOCSETP, &tty_buf);
  526.     } else {
  527.         tty_buf.sg_flags = tsave_flags;
  528.         ioctl(0, TIOCSETP, &tty_buf);
  529.     }
  530. #endif /* UNIX_BSD4_2 */
  531. #ifdef UNIX_SYSV
  532.     struct termio tty_buf;
  533.     static struct termio tty_save;
  534.  
  535.     if (on) {
  536.         ioctl(0, TCGETA, &tty_buf);
  537.         tty_save = tty_buf;
  538.         tty_buf.c_lflag &= ~(ICANON | ECHO);
  539.         tty_buf.c_oflag &= ~ONLCR;
  540.         tty_buf.c_cc[4] = 1;  /* MIN */
  541.         tty_buf.c_cc[5] = 2;  /* TIME */
  542.         ioctl(0, TCSETAF, &tty_buf);
  543.     } else {
  544.         ioctl(0, TCSETAF, &tty_save);
  545.     }
  546. #endif /* UNIX_SYSV */
  547. }
  548.  
  549. /* md_gdtcf(): (Get Default Termcap File)
  550.  *
  551.  * This function is called ONLY when the program is compiled with CURSES
  552.  * defined.  If you use your system's curses/termlib library, this function
  553.  * won't be called.  On most UNIX systems, "/etc/termcap" suffices.
  554.  *
  555.  * If their is no such termcap file, then return 0, but in that case, you
  556.  * must have a TERMCAP file returned from md_getenv("TERMCAP").  The latter
  557.  * will override the value returned from md_gdtcf().  If the program is
  558.  * compiled with CURSES defined, and md_gdtcf() returns 0, and
  559.  * md_getenv("TERMCAP") returns 0, the program will have no terminal
  560.  * capability information and will quit.
  561.  */
  562.  
  563. char *
  564. md_gdtcf()
  565. {
  566.     return("/etc/termcap");
  567. }
  568.  
  569. /* md_tstp():
  570.  *
  571.  * This function puts the game to sleep and returns to the shell.  This
  572.  * only applies to UNIX 4.2 and 4.3.  For other systems, the routine should
  573.  * be provided as a do-nothing routine.  md_tstp() will only be referenced
  574.  * in the code when compiled with CURSES defined.
  575.  *
  576.  */
  577.  
  578. md_tstp()
  579. {
  580. #ifdef UNIX_BSD4_2
  581.     kill(0, SIGTSTP);
  582. #endif /* UNIX_BSD4_2 */
  583. }
  584.  
  585. #endif /* CURSES */
  586.  
  587. #endif /* UNIX */
  588.